package utils

import (
	"fmt"
	"io"
	"sync"
	"sync/atomic"
	"time"
)

type IOHandle struct {
	disFlag     int32
	closedFlag  int32
	startedFlag int32
	closeWg     sync.WaitGroup
	disReason   string

	recvBlock []byte

	reconnectInterval time.Duration

	connecttime time.Time

	connstr string
	argsMap StrMap
	conn    io.ReadWriteCloser

	OnConnectFail  func(sender *IOHandle, err error)
	OnConnected    func(sender *IOHandle)
	OnDisconnected func(sender *IOHandle, reason string)

	/*
	  buf 需要异步使用需要进行copy
	*/
	OnRecv func(sender *IOHandle, buf []byte)

	OpenFunc func(argsMap StrMap) (conn io.ReadWriteCloser, err error)

	TagPtr interface{}
}

func NewIOHandle() *IOHandle {
	return &IOHandle{reconnectInterval: time.Second}
}

func (this *IOHandle) doConnectFail(err error) {
	evt := this.OnConnectFail
	if evt != nil {
		evt(this, err)
	}
}

func (this *IOHandle) doConnected() {
	evt := this.OnConnected
	if evt != nil {
		evt(this)
	}
}

func (this *IOHandle) doDisconnected() {
	evt := this.OnDisconnected
	if evt != nil {
		evt(this, this.disReason)
	}
}

func (this *IOHandle) doRecv(buf []byte) {
	evt := this.OnRecv
	if evt != nil {
		evt(this, buf)
	}
}

func (this *IOHandle) Close() error {
	atomic.StoreInt32(&this.closedFlag, 1)
	this.RequestDis("关闭服务")
	this.closeWg.Wait()
	return nil
}

func (this *IOHandle) CheckStart() {
	if atomic.CompareAndSwapInt32(&this.startedFlag, 0, 1) {
		this.closeWg.Add(1)
		go this.handleRecv()
	}
}

func (this *IOHandle) RequestDis(reason string) {
	conn := this.conn
	if atomic.CompareAndSwapInt32(&this.disFlag, 0, 1) {
		this.disReason = reason
		if conn != nil {
			conn.Close()
		}
	}
}

func (this *IOHandle) SetConnStr(connstr string) (isChanged bool) {
	if this.connstr != connstr {
		this.connstr = connstr
		strmap := NewStrMap0()
		strmap.URLFormDecode(connstr)
		this.argsMap = strmap
		this.RequestDis("配置更新")
		return true
	}
	return false
}

func (this *IOHandle) checkConnect() bool {
	if this.conn != nil {
		return true
	}

	if time.Since(this.connecttime) < this.reconnectInterval {
		return false
	}
	this.connecttime = time.Now()
	fn := this.OpenFunc
	if fn == nil {
		this.doConnectFail(fmt.Errorf("缺少连接事件函数(OpenFunc)"))
		return false
	}
	strMap := this.argsMap
	if strMap == nil {
		this.doConnectFail(fmt.Errorf("缺少连接参数配置"))
		return false
	}
	conn, err := fn(strMap)
	if err != nil {
		this.doConnectFail(err)
		return false
	}
	this.conn = conn
	atomic.StoreInt32(&this.disFlag, 0)
	this.reconnectInterval = time.Duration(strMap.IntByName("reconnectInterval", 1)) * time.Second
	l := strMap.IntByName("block", 1024)
	if l <= 0 {
		l = 1024
	}
	if len(this.recvBlock) != l {
		this.recvBlock = make([]byte, l)
	}
	this.doConnected()
	return true
}

func (this *IOHandle) Write(buf []byte) (n int, err error) {
	conn := this.conn
	if conn == nil {
		return -1, io.ErrClosedPipe
	}
	return conn.Write(buf)
}

func (this *IOHandle) handleRecv() {
	defer func() {
		this.startedFlag = 0
		this.closeWg.Done()
	}()

	for this.closedFlag == 0 {
		if !this.checkConnect() {
			time.Sleep(time.Millisecond * 100)
			continue
		}
		conn := this.conn

		n, err := conn.Read(this.recvBlock)
		if err != nil {
			this.RequestDis(err.Error())
			this.conn = nil
			this.doDisconnected()
			continue
		}
		this.doRecv(this.recvBlock[:n])
	}
}
